import axios from 'axios'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import { getToken, setToken } from './cache'
import router from '@/routes'
import useUserStore from '@/stores/modules/user'
import { UserTokenEnum } from '@/stores/type'
import { LOGIN_PATH } from '@/routes/routes'
import { NETWORK_STATUS } from '@/config/map'
import { ENV_CONFIG } from '@/config'
import type { IUserInfoResp } from '@/layouts/user-layout/type'

const axiosInterface = axios.create({
  baseURL: (ENV_CONFIG as any)[import.meta.env.MODE].baseApi,
  timeout: 60000,
  headers: {
    'content-type': 'application/json',
  },
})

// 缓存 token 过期后的请求函数
let catchRequestFunc: Array<() => void> = []
let refreshing = false

// 请求拦截
axiosInterface.interceptors.request.use((config: any) => {
  const token = getToken(UserTokenEnum.ACCESS_TOKEN)
  if (token) {
    const { headers } = config
    headers!.Authorization = `Bearer ${token}`
  }
  return config
})

// 响应拦截
axiosInterface.interceptors.response.use(
  // 响应成功
  async (response: AxiosResponse<API.BaseResponseType<any>>) => {
    const { status, data, config } = response
    if (status === 200) {
      const { code, message } = data
      const responseCode = Number(code)

      // 处理失败
      if (responseCode === 401 && !config.url?.includes('/user/refresh')) {
        // 缓存过期后的请求函数
        // eslint-disable-next-line no-new
        new Promise((resolve) => {
          catchRequestFunc.push(() => {
            resolve(request(response.config))
          })
        })
        if (!refreshing)
          await handleRefreshToken()
      }
      else if (responseCode === 403) {
        // window.$message.error(message)
        modalError('Server Error', message)
        router.push({
          name: 'login',
        })
      }
      else if (responseCode !== 200) {
        // TODO2: 这里添加非认证错误
        modalError('Server Error', message)
      }
    }
    return response
  },
  // 响应失败
  async ({ response }) => {
    // 失败前的特殊处理(如果没有提前return最后会执行到Promise.reject中断执行)
    const { status, data, config } = response

    if (status === 401 && !config.url.includes('/user/refresh')) {
      // eslint-disable-next-line no-new
      new Promise((resolve) => {
        catchRequestFunc.push(() => {
          resolve(request(response.config))
        })
      })

      if (!refreshing)
        await handleRefreshToken()
    }
    else {
      const msg = data?.message
      modalError(String(status).startsWith('4') ? 'Client Error' : 'Server Error', msg || NETWORK_STATUS[status])
    }
  },
)

function modalError(title: string, content?: string) {
  window.$modal.error({
    title,
    content: content || '',
    okText: 'Confirm',
    onOk() {
      window.$modal.destroyAll()
    },
  })
}

async function handleRefreshToken() {
  const refreshToken = getToken(UserTokenEnum.REFRESH_TOKEN)
  const userStore = useUserStore()

  const _handleReLogin = async () => {
    await userStore.resetToken()
    modalError('Token Expired', 'Your identity has expired, please log in again')
    catchRequestFunc = []
    router.push({
      path: LOGIN_PATH,
    })
  }

  if (refreshToken) {
    try {
      refreshing = true
      const { code, data } = await request<{
        token: string
        refreshToken: string
        data: IUserInfoResp
      }>({
        url: '/user/refresh',
        method: 'get',
        params: {
          refresh_token: getToken(UserTokenEnum.REFRESH_TOKEN),
        },
      })
      refreshing = false
      if (Number(code) === 200) {
        setToken(UserTokenEnum.ACCESS_TOKEN, data.token)
        setToken(UserTokenEnum.REFRESH_TOKEN, data.refreshToken)
        axiosInterface.defaults.headers.common.Authorization = `Bearer ${data.token}`

        // 执行 token 失效后缓存的请求函数
        catchRequestFunc.forEach(async (catchFunc) => {
          await catchFunc()
        })
      }
    }
    catch {
      refreshing = false
      // refreshtoken 也过期了
      _handleReLogin()
    }
  }
  else {
    // 不存在refresh token, 跳登录页
    _handleReLogin()
  }
}

// 对外暴露 request 请求函数
async function request<T>(config: AxiosRequestConfig): Promise<API.BaseResponseType<T>> {
  try {
    const { data } = await axiosInterface(config)
    return data
  }
  catch (error) {
    // 响应失败中断后续代码执行
    return Promise.reject(error)
  }
}

export default request
